package com.woniu.utils;

import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.async.ResultCallback;
import com.github.dockerjava.api.command.*;
import com.github.dockerjava.api.exception.ConflictException;
import com.github.dockerjava.api.exception.DockerException;
import com.github.dockerjava.api.exception.NotFoundException;
import com.github.dockerjava.api.model.*;
import com.woniu.entity.CtfContainer;
import com.woniu.entity.CtfRange;
import com.woniu.entity.dto.ContainerRuntimeInfo;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.codec.multipart.FilePart;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;

import java.awt.image.DataBuffer;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.InetAddress;
import java.util.*;
import java.io.IOException;
import java.net.ServerSocket;
import java.util.stream.Collectors;

/**
 * @Author hq
 * @Date 2025/6/10$ 9:59$
 * @Description TODO
 * @Params $
 * @Return $
 **/
@Component
public class DockerUtils {
    @Autowired
    private DockerClient client; // 通过 Spring 注入
    @Value("${docker.host}")
    private String HostIp;

    /**
     * 加载Docker镜像并自动标记为latest
     *
     * @param targetRangeFile 镜像文件
     * @param targetRangeName 镜像名称
     * @return 镜像信息
     */
    public CtfRange loadDockerImageFromTar(InputStream targetRangeFile, String targetRangeName) {
        CtfRange ctfRange = new CtfRange();
        try {
//            TarArchiveInputStream tarIs = new TarArchiveInputStream(tmp);
//            TarArchiveEntry entry;
//            while ((entry = tarIs.getNextTarEntry()) != null) {
//                if (!entry.isDirectory()&&"mainfest.json".equals(entry.getName())) {
//                    System.out.println("文件名：" + entry.getName());
//
//                    // 若要读取文件内容，可从 tarIs 流中读取：tarIs.read(...)
//                }
//            }
            //第一个现有的镜像
            List<Image> images = client.listImagesCmd().exec();
            LoadImageCmd loadImageCmd = client.loadImageCmd(targetRangeFile);
            loadImageCmd.exec();
            // 2. 获取镜像信息（添加延迟确保镜像加载完成）
//            Thread.sleep(1000); // 简单处理，实际应使用重试机制
            List<Image> imagesSecond = client.listImagesCmd().exec();
            imagesSecond.removeAll(images);
            if (images.isEmpty()) {
                throw new RuntimeException("镜像加载后未找到");
            }
            Image image = imagesSecond.get(0);
            String imageId = image.getId();
            String[] repoTags = image.getRepoTags();
            String uniqueTag = UUID.randomUUID().toString().split("-")[0];
            if (repoTags == null || repoTags.length == 0) {
                // 4. 对新镜像打标签
                client.tagImageCmd(imageId, targetRangeName, uniqueTag).exec();
                throw new RuntimeException("镜像未标记，没有名字");
            }
            // 5. 设置返回信息
            ctfRange.setRangeId(imageId.replace("sha256:", ""));
//            ctfRange.setRangeId(imageId);
            ctfRange.setRangeName(targetRangeName + ":" + uniqueTag); // 固定格式: name:latest
            return ctfRange;
        } catch (Exception e) {
            throw new RuntimeException("❌ 加载Docker镜像失败: " + e.getMessage(), e);
        }
    }



        private String addUniqueFileToImage(String imageId, String uniqueId) {
        try {
            // 1. 创建并启动容器
            CreateContainerResponse container = client.createContainerCmd(imageId).exec();
            client.startContainerCmd(container.getId()).exec();

            // 2. 写入唯一标识文件
            String[] cmd = {"/bin/sh", "-c", "echo '" + uniqueId + "' > /unique_id.txt"};
            ExecCreateCmdResponse execCmd = client.execCreateCmd(container.getId())
                    .withCmd(cmd)
                    .exec();
            client.execStartCmd(execCmd.getId()).exec(new ResultCallback.Adapter<>());

            // 3. 提交容器为新镜像
            String newImageId = client.commitCmd(container.getId()).exec(); // 返回是镜像ID字符串
            System.out.println("✅ 镜像提交成功，新镜像ID: " + newImageId);

            // 4. 停止并删除临时容器
            client.stopContainerCmd(container.getId()).exec();
            client.removeContainerCmd(container.getId()).exec();

            return newImageId;

        } catch (Exception e) {
            throw new RuntimeException("❌ 添加唯一标识文件失败: " + e.getMessage(), e);
        }
    }



    /**
     * 删除镜像（默认强制删除）
     * @param imageId 镜像ID（支持完整ID或短ID）
     * @throws DockerException 删除失败时抛出
     */
    public void removeImage(String imageId) throws DockerException {
        removeImage(imageId, true); // 默认强制删除
    }

    /**
     * 删除镜像（可选择是否强制）
     * @param imageId 镜像ID
     * @param force 是否强制删除（强制模式下会自动处理关联容器）
     */
    public void removeImage(String imageId, boolean force) throws DockerException {
        try {
            // 1. 标准化镜像ID（兼容sha256:前缀和短ID）
            String normalizedId = imageId.replace("sha256:", "");
            // 3. 执行删除（强制模式+清理未使用层）
            client.removeImageCmd(normalizedId)
                    .withForce(force)      // 始终启用强制删除
                    .exec();
            System.out.println("✅ 镜像已删除: " + normalizedId);
        } catch (NotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    private boolean imageExists(String normalizedId) {
        List<Image> images = client.listImagesCmd()
                .withImageNameFilter(normalizedId)
                .exec();
                return !images.isEmpty();
    }

    /**
     * 停止并删除使用指定镜像的所有容器
     */
    private void stopAndRemoveContainersUsingImage(String imageId) {
        List<Container> containers = client.listContainersCmd()
                .withShowAll(true) // 包含已停止的容器
                .exec()
                .stream()
                // 模糊匹配镜像ID（兼容长短ID）
                .filter(c -> c.getImageId().contains(imageId))
                .collect(Collectors.toList());

        containers.forEach(container -> {
            try {
                // 停止运行中的容器
                if ("running".equals(container.getState())) {
                    client.stopContainerCmd(container.getId()).exec();
                    System.out.println("🛑 已停止容器: " + container.getId());
                }

                // 强制删除容器（包括已停止的）
                client.removeContainerCmd(container.getId())
                        .withForce(true)
                        .withRemoveVolumes(true) // 同时删除关联的匿名卷
                        .exec();

                System.out.println("🗑️ 已删除容器: " + container.getId());
            } catch (Exception e) {
                System.err.println("⚠️ 处理容器失败 " + container.getId() + ": " + e.getMessage());
            }
        });
    }


    /**
     * 创建容器放入flag并启动
     */
    public CtfContainer createAndStartContainerWithFlag( String imageId,
                                                         String flagPath,
                                                         String containerName,
                                                         int containerPort,
                                                         String flagContent){
        CtfContainer container = createContainerWithFlag(imageId, flagPath,containerName, containerPort, flagContent);
        startContainer(container.getContainerId());
        return container;
    }
    /**
     * 创建容器并自动分配可用端口 (直接写入flag内容)
     * @param imageId       镜像ID
     * @param containerName 容器名称
     * @param containerPort 容器内部端口
     * @param flagContent   flag内容(直接传入)
     * @return 容器创建结果
     */
    public CtfContainer createContainerWithFlag(
            String imageId,
            String flagPath,       // 新增：flag 存放路径（如 "/root" 或 "/tmp"）
            String containerName,
            int containerPort,
            String flagContent) throws DockerException {

        try {
            // 1. 检查镜像是否存在
//            List<Image> images = client.listImagesCmd()
//                    .withReferenceFilter(imageId)
//                    .exec();
//            if (images.isEmpty()) {
//                throw new RuntimeException("❌ 镜像不存在: " + imageId);
//            }

            // 2. 获取并锁定可用端口
            int hostPort = getAvailablePort();

            // 3. 创建容器
            CreateContainerResponse response = client.createContainerCmd(imageId)
                    .withName(containerName.replaceAll(":",""))
                    .withExposedPorts(ExposedPort.tcp(containerPort))
                    .withPortBindings(new PortBinding(Ports.Binding.bindPort(hostPort), ExposedPort.tcp(containerPort)))
                    .exec();

            if (response.getId() == null) {
                throw new RuntimeException("容器创建失败");
            }

            // 4. 启动容器
            client.startContainerCmd(response.getId()).exec();

            // 5. 写入 flag 到指定路径
            writeFlagToContainer1(response.getId(), flagPath, flagContent);

            // 6. 构造返回对象
            CtfContainer ctfContainer = new CtfContainer();
            ctfContainer.setContainerId(response.getId().substring(0, 12));
            String ip = HostIp.replace("tcp://", "").replace(":2375", "");
            //去掉sha:
            ctfContainer.setRangeId(imageId.replace("sha256:", ""));
            ctfContainer.setSocket(ip + ":" + hostPort);
            ctfContainer.setFlagValue(flagContent);
            ctfContainer.setCreateTime(new Date());
            return ctfContainer;
        } catch (Exception e) {
            throw new RuntimeException("❌ 创建容器失败: " + e.getMessage(), e);
        }
    }

    /**
     * 写入flag内容到容器
     */
    private void writeFlagToContainer1(String containerId,String flagPath, String flagContent) {
        if(flagPath .equals("/root")){
            try {
                // 创建目录(如果需要)
                String[] mkdirCmd = {"/bin/sh", "-c", "mkdir -p /root && chmod 700 /root"};
                ExecCreateCmdResponse mkdirResponse = client.execCreateCmd(containerId)
                        .withCmd(mkdirCmd)
                        .exec();
                client.execStartCmd(mkdirResponse.getId())
                        .exec(new ResultCallback.Adapter<>());
                // 写入flag内容
                String[] writeCmd = {"/bin/sh", "-c", "echo '" + flagContent.replace("'", "'\\''") + "' > /root/flag.txt"};
                ExecCreateCmdResponse writeResponse = client.execCreateCmd(containerId)
                        .withCmd(writeCmd)
                        .exec();
                client.execStartCmd(writeResponse.getId())
                        .exec(new ResultCallback.Adapter<>());
            } catch (Exception e) {
                throw new RuntimeException("❌ 写入flag到容器失败: " + e.getMessage(), e);
            }
        }else {
            try {
                // 创建目录(如果需要)
                String[] mkdirCmd = {"/bin/sh", "-c", "mkdir -p /tmp && chmod 700 /tmp"};
                ExecCreateCmdResponse mkdirResponse = client.execCreateCmd(containerId)
                        .withCmd(mkdirCmd)
                        .exec();
                client.execStartCmd(mkdirResponse.getId())
                        .exec(new ResultCallback.Adapter<>());
                // 写入flag内容
                String[] writeCmd = {"/bin/sh", "-c", "echo '" + flagContent.replace("'", "'\\''") + "' > /tmp/flag.txt"};
                ExecCreateCmdResponse writeResponse = client.execCreateCmd(containerId)
                        .withCmd(writeCmd)
                        .exec();
                client.execStartCmd(writeResponse.getId())
                        .exec(new ResultCallback.Adapter<>());
            } catch (Exception e) {
                throw new RuntimeException("❌ 写入flag到容器失败: " + e.getMessage(), e);
            }
        }

    }

    /**
     * 启动容器
     */
    public ContainerRuntimeInfo startContainer(String containerId) throws DockerException {
        try {
            InspectContainerResponse inspect = client.inspectContainerCmd(containerId).exec();
            // 状态检查
            if (inspect.getState().getRunning()) {
                System.out.println("Container already running: {}" + containerId);
                return buildRuntimeInfo(inspect);
            }
            // 执行启动
            client.startContainerCmd(containerId).exec();
            System.out.println("Container started: {}" + containerId);

            // 获取最新状态
            inspect = client.inspectContainerCmd(containerId).exec();
            return buildRuntimeInfo(inspect);
        } catch (Exception e) {
            System.out.println("Failed to start container: {}" + e.getMessage());
            throw new RuntimeException("❌ 启动容器失败: " + e.getMessage(), e);
        }
    }
    //关闭容器
    public void stopContainer(String containerIdOrName) {
        try {
            if (containerIdOrName == null || containerIdOrName.trim().isEmpty()) {
                System.out.println("❌ 容器ID为空");
                return;
            }

            if (!containerExists(containerIdOrName)) {
                System.out.println("❌ 容器不存在，无法停止: " + containerIdOrName);
                return;
            }
            client.stopContainerCmd(containerIdOrName).exec();
            System.out.println("✅ 容器已停止: " + containerIdOrName);

        } catch (Exception e) {
            System.out.println("❌ 停止容器失败: " + e.getMessage());
        }
    }
    /**
     * 删除容器（默认强制删除）
     * @param containerId 容器ID/名称
     * @throws DockerException 如果删除失败
     */
    public void removeContainer(String containerId) throws DockerException {
        if (!containerExists(containerId)) {
            throw new NotFoundException("容器不存在: " + containerId);
        }

        try {
            // 先尝试停止容器（避免删除失败）
            try {
                InspectContainerResponse inspect = client.inspectContainerCmd(containerId).exec();
                if (inspect.getState().getRunning()) {
                    client.stopContainerCmd(containerId).exec();
                }
            } catch (Exception e) {
                System.out.println("⚠️ 停止容器时发生非致命错误: " + e.getMessage());
            }

            // 强制删除容器（包括已停止但未删除的容器）
            client.removeContainerCmd(containerId)
                    .withForce(true)    // 强制删除
                    .withRemoveVolumes(true)  // 同时删除关联的匿名卷
                    .exec();

            System.out.println("✅ 容器已强制删除: " + containerId);
        } catch (NotFoundException e) {
            throw new DockerException("容器不存在或已被删除: " + containerId, 404);
        } catch (ConflictException e) {
            throw new DockerException("容器删除冲突，可能仍有进程占用: " + containerId, 409);
        } catch (Exception e) {
            throw new DockerException("删除容器失败: " + e.getMessage(), 500);
        }
    }
//---------- 辅助方法 ----------//
    private ContainerRuntimeInfo buildRuntimeInfo(InspectContainerResponse inspect) {
        String containerId = inspect.getId();
        String containerName = inspect.getName();
        String status = inspect.getState().getStatus();
        String ipAddress = inspect.getNetworkSettings().getIpAddress();
        String hostPort = null;
        Ports ports = inspect.getNetworkSettings().getPorts();
        if (ports != null && ports.getBindings() != null) {
            Map<ExposedPort, Ports.Binding[]> bindings = ports.getBindings();
            // 尝试找任意一个有效的端口绑定
            for (Map.Entry<ExposedPort, Ports.Binding[]> entry : bindings.entrySet()) {
                if (entry.getValue() != null && entry.getValue().length > 0 &&
                        entry.getValue()[0] != null && entry.getValue()[0].getHostPortSpec() != null) {
                    hostPort = entry.getValue()[0].getHostPortSpec();
                    break;
                }
            }
            // 如果没找到，尝试查找 HTTP 常见端口（如 80）
            if (hostPort == null) {
                Ports.Binding[] httpBinding = bindings.get(ExposedPort.tcp(80));
                if (httpBinding != null && httpBinding.length > 0 && httpBinding[0] != null) {
                    hostPort = httpBinding[0].getHostPortSpec();
                }
            }
        }
        if (hostPort == null) {
            hostPort = "未知";
        }
        return new ContainerRuntimeInfo(
                containerId,
                containerName,
                status,
                ipAddress,
                hostPort,
                "http://" + getHostIp() + ":" + hostPort
        );
    }

    private String getHostIp() {
        try {
            return InetAddress.getLocalHost().getHostAddress();
        } catch (Exception e) {
            return "localhost";
        }
    }

    /**
     * 检查端口是否可用
     */
    private boolean isPortAvailable(int port) {
        try (ServerSocket serverSocket = new ServerSocket(port)) {
            return true;
        } catch (IOException e) {
            return false;
        }
    }

    /**
     * 获取一个可用的随机端口
     */
    private int getAvailablePort(int minPort, int maxPort) {
        Random random = new Random();
        int attempts = 0;
        int maxAttempts = 100; // 最大尝试次数

        while (attempts < maxAttempts) {
            int port = minPort + random.nextInt(maxPort - minPort + 1);
            if (isPortAvailable(port)) {
                return port;
            }
            attempts++;
        }
        throw new RuntimeException("无法找到可用端口，请检查端口范围或系统配置");
    }

    /**
     * 获取一个可用的随机端口（使用默认范围）
     */
    private int getAvailablePort() {
        return getAvailablePort(10000, 60000); // 使用10000-60000范围的端口
    }

    public void printAllContainers() {
        try {
            List<Container> containers = client.listContainersCmd().exec();
            System.out.println("当前运行中的容器列表:");
            for (Container container : containers) {
                System.out.printf("ID: %s, Names: %s, Status: %s%n",
                        container.getId(), container.getNames(), container.getStatus());
            }
        } catch (Exception e) {
            System.out.println("❌ 获取容器列表失败: " + e.getMessage());
        }
    }

    public boolean containerExists(String containerId) {
        try {
            InspectContainerResponse response = client.inspectContainerCmd(containerId).exec();
            return response != null;
        } catch (Exception e) {
            return false;
        }
    }


    // 停止并删除容器
    public void stopAndRemoveContainer(String containerId) {
        try {
            client.stopContainerCmd(containerId).exec();
            client.removeContainerCmd(containerId).exec();
            System.out.println("✅ 容器已销毁: " + containerId);
        } catch (Exception e) {
            System.err.println("❌ 销毁容器失败: " + containerId);
            e.printStackTrace();
        }
    }

    //查看容器状态
    public ContainerRuntimeInfo inspectContainer(String containerId) {
        try {
            InspectContainerResponse inspect = client.inspectContainerCmd(containerId).exec();
            return buildRuntimeInfo(inspect);
        } catch (Exception e) {
            System.err.println("❌ 获取容器信息失败: " + containerId);
            e.printStackTrace();
            return null;
        }
    }

}
